#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 边的结构体
struct Edge {
    int from; // 起始城镇编号
    int to; // 目标城镇编号
    int cost; // 道路改建的预算成本

    Edge(int f, int t, int c) : from(f), to(t), cost(c) {}
};

// 图的结构体
struct Graph {
    int numVertices; // 城镇数目
    vector<Edge> edges; // 边的集合

    Graph(int numV) : numVertices(numV) {}

    // 添加道路
    void addRoad(int from, int to, int cost) {
        edges.emplace_back(from, to, cost);//可以避免push_back的拷贝再添加
    }

    // 查找节点的根节点（使用路径压缩优化）
    int findRoot(vector<int>& parent, int node) {
        if (parent[node] != node)
            parent[node] = findRoot(parent, parent[node]);
        return parent[node];
    }

    // 合并两个节点的集合（使用按秩合并优化）
    void mergeSets(vector<int>& parent, vector<int>& rank, int root1, int root2) {
        if (rank[root1] < rank[root2])
            parent[root1] = root2;
        else if (rank[root1] > rank[root2])
            parent[root2] = root1;
        else {
            parent[root2] = root1;
            rank[root1]++;
        }
    }

    // Kruskal算法求最小生成树的总成本
    int kruskal() {
        // 按边的成本从小到大排序
        sort(edges.begin(), edges.end(), [](const Edge& e1, const Edge& e2) {
            return e1.cost < e2.cost;
        });

        vector<int> parent(numVertices + 1); // 并查集的父节点
        vector<int> rank(numVertices + 1, 0); // 并查集的秩

        // 初始化并查集
        for (int i = 1; i <= numVertices; i++) {
            parent[i] = i;
        }

        int minCost = 0; // 最小生成树的总成本
        int numEdges = 0; // 当前已选择的边数

        // 遍历每条边
        for (const auto& edge : edges) {
            int root1 = findRoot(parent, edge.from);
            int root2 = findRoot(parent, edge.to);

            // 判断边的两个节点是否属于同一个集合，即是否连通
            if (root1 != root2) {
                mergeSets(parent, rank, root1, root2);
                minCost += edge.cost;
                numEdges++;
            }

            // 如果已选择的边数等于城镇数目减1，则已经构建完成最小生成树
            if (numEdges == numVertices - 1)
                break;
        }

        if (numEdges < numVertices - 1)
            return -1; // 无法保证畅通
        else
            return minCost;
    }
};

int main() {
    int numVillages, numRoads;
    cin >> numVillages >> numRoads;

    Graph graph(numVillages);

    for (int i = 0; i < numRoads; i++) {
        int from, to, cost;
        cin >> from >> to >> cost;
        graph.addRoad(from, to, cost);
    }

    int minCost = graph.kruskal();
    cout << minCost << endl;

    return 0;
}
